/***************************************************************************
 * Copyright 2012-2013 TXT e-solutions SpA
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * This work was performed within the IoT_at_Work Project
 * and partially funded by the European Commission's
 * 7th Framework Programme under the contract ICT-257367.
 *
 * Authors:
 *      Salvatore Piccione (TXT e-solutions SpA)
 *
 * Contributors:
 *        Domenico Rotondi (TXT e-solutions SpA)
 **************************************************************************/
package it.txt.ens.client.util.parser;

import it.txt.ens.client.core.ENSBrokerConnectionParameters;
import it.txt.ens.client.core.factory.ENSBrokerConnectionParametersFactory;
import it.txt.ens.core.util.parser.ParseException;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Dictionary;
import java.util.List;

/**
 * This class provides methods to create instances of {@link ENSBrokerConnectionParameters}
 * using the set of (key, value) pairs stored in dictionaries.
 * 
 * @author Salvatore Piccione (TXT e-solutions SpA - salvatore.piccione AT txtgroup.com)
 * @author Domenico Rotondi (TXT e-solutions SpA - domenico.rotondi AT txtgroup.com)
 *
 */
public class ENSBrokerConnectionParametersDictionaryParser {
    public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS Z";
    
    /**
     * Creates an instance of {@link ENSBrokerConnectionParameters} from the dictionary
     * <code>propertyDictionary</code> and using the factory <code>factory</code>
     * @param factory the factory to be used to create the object
     * @param propertyDictionary the set of (key, value) pairs storing the data to be used
     * @return an instance of {@code ENSBrokerConnectionParameters}
     * @throws ParseException if at least one mandatory property without a default value is missing or at least
     * one property is not valid
     * @throws IllegalArgumentException if at least one parameter is <code>null</code> or
     * <code>propertyDictionary</code> is empty. 
     */
    public static ENSBrokerConnectionParameters parse (ENSBrokerConnectionParametersFactory factory,
            Dictionary<?,?> propertyDictionary) throws ParseException, IllegalArgumentException {
        if (factory == null)
            throw new IllegalArgumentException("The factory cannot be null");
        if (propertyDictionary == null)
            throw new IllegalArgumentException("The property container cannot be null");
        if (propertyDictionary.isEmpty())
            throw new IllegalArgumentException("The property container cannot be empty");
        
        List<String> missingProperties = new ArrayList<String>(
                ENSBrokerConnectionParametersFactory.NUMBER_OF_MANDATORY_PROPERTIES);
        try {

            String subjectID;
            if ((subjectID = (String) propertyDictionary.get(ENSBrokerConnectionParametersFactory.SUBJECT_ID)) == null)
                missingProperties.add(ENSBrokerConnectionParametersFactory.SUBJECT_ID);
            
            String accessToken;
            if ((accessToken = (String) propertyDictionary.get(ENSBrokerConnectionParametersFactory.ACCESS_TOKEN)) == null)
                missingProperties.add(ENSBrokerConnectionParametersFactory.ACCESS_TOKEN);
            
            String destinationName;
            if ((destinationName = (String) propertyDictionary.get(ENSBrokerConnectionParametersFactory.DESTINATION_NAME)) == null)
                missingProperties.add(ENSBrokerConnectionParametersFactory.DESTINATION_NAME);
                
            String sessionToken;
            if ((sessionToken = (String) propertyDictionary.get(ENSBrokerConnectionParametersFactory.SESSION_TOKEN)) == null)
                missingProperties.add(ENSBrokerConnectionParametersFactory.SESSION_TOKEN);
            
            if (!missingProperties.isEmpty()) {
                throw new ParseException(missingProperties);
            }
            
            //port parse
            Object portObj = propertyDictionary.get(ENSBrokerConnectionParametersFactory.BROKER_PORT);
            int port;
            if (portObj == null)
                port = ENSBrokerConnectionParametersFactory.DEFAULT_BROKER_PORT;
            else if (portObj instanceof String) {
                try {
                    port = Integer.parseInt((String) portObj);
                } catch (NumberFormatException e) {
                    throw new ParseException("The value of the property " + ENSBrokerConnectionParametersFactory.BROKER_PORT + 
                        " must be a valid integer number. Given value: " + portObj);
                }
            } else if (portObj instanceof Integer)
                port = (Integer) portObj;
            else
                throw new ParseException("The value of the property " + ENSBrokerConnectionParametersFactory.BROKER_PORT + 
                    " must be an instance of either " + Integer.class.getName() +
                    " or " + String.class.getName() + ". Value class: " + 
                    portObj.getClass().getName());
            
            //mgmt parse
            portObj = propertyDictionary.get(ENSBrokerConnectionParametersFactory.MGMT_PORT);
            int mgmtPort;
            if (portObj == null)
                mgmtPort = ENSBrokerConnectionParametersFactory.DEFAULT_MGMT_PORT;
            else if (portObj instanceof String) {
                try {
                    mgmtPort = Integer.parseInt((String) portObj);
                } catch (NumberFormatException e) {
                    throw new ParseException("The value of the property " + ENSBrokerConnectionParametersFactory.MGMT_PORT + 
                        " must be a valid integer number. Given value: " + portObj);
                }
            } else if (portObj instanceof Integer)
                mgmtPort = (Integer) portObj;
            else
                throw new ParseException("The value of the property " + ENSBrokerConnectionParametersFactory.MGMT_PORT + 
                    " must be an instance of either " + Integer.class.getName() +
                    " or " + String.class.getName() + ". Value class: " + 
                    portObj.getClass().getName());
            
            //date parse
            Object sessionExpObj = propertyDictionary.get(ENSBrokerConnectionParametersFactory.SESSION_EXPIRATION);
            Date sessionExpiration;
            if (sessionExpObj == null)
                sessionExpiration = null;
            else if (sessionExpObj instanceof Long)
                sessionExpiration = new Date((Long) sessionExpObj);
            else if (sessionExpObj instanceof String) {
                try {
                    sessionExpiration = new SimpleDateFormat(DATE_FORMAT).parse((String) sessionExpObj);
                } catch (java.text.ParseException e) {
                    throw new ParseException("An error occurred while parsing the given " +
                        "session expiration date using the pattern '" + DATE_FORMAT + "'", e);
                }
            } else
                throw new ParseException("The value of the property '" + ENSBrokerConnectionParametersFactory.SESSION_EXPIRATION + 
                    "' must be an instance of either " + Long.class.getName() +
                    " or " + String.class.getName() + ". Value class: " + 
                    portObj.getClass().getName());
            
            return factory.create(subjectID, accessToken, (String) propertyDictionary.get(ENSBrokerConnectionParametersFactory.BROKER_HOST),
                    port, mgmtPort, (String) propertyDictionary.get(ENSBrokerConnectionParametersFactory.VIRTUAL_HOST), (String) destinationName, 
                    Boolean.parseBoolean((String) propertyDictionary.get(ENSBrokerConnectionParametersFactory.TLS_ENABLED)),
                    sessionExpiration, sessionToken);
        } catch (ClassCastException e) {
            throw new ParseException("An error occurred while casting the value of a property",e);
        }
    }
}
